package org.cogaen.java2d.xml;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

public class AnimationParser {

	private static final String ANIMATION_ELEMENT = "animation";
	private static final double DEFAULT_DIMENSION = 0.0;
	private static final double DEFAULT_DISPLAYTIME = 1.0 / 15.0;
	private static final int DEFAULT_ARRANGEMENT = 1;
	private static final String IMAGE_ATTRIB = "image";
	private static final String NAME_SPACE = null;
	private static final String WIDTH_ATTRIB = "width";
	private static final String HEIGHT_ATTRIB = "height";
	private static final String FRAMERATE_ATTRIB = "framerate";
	private static final String COLUMNS_ATTRIB = "columns";
	private static final String ROWS_ATTRIB = "rows";
	private static final String FIRST_FRAME_ATTRIB = "first";
	private static final String NUM_FRAMES_ATTRIB = "nframes";
	private double width;
	private double height;
	private String imageName;
	private double displayTime;
	private int columns;
	private int rows;
	private int nFrames;
	private int firstFrame;

	public static boolean isStart(XMLStreamReader reader) {
		return reader.isStartElement() && ANIMATION_ELEMENT.equals(reader.getLocalName());
	}
	
	public static ResetableStreamFilter createFilter(String colorName) {
		return new ElementFilter(ANIMATION_ELEMENT, colorName);
	}

	private void reset() {
		this.width = DEFAULT_DIMENSION;
		this.height = DEFAULT_DIMENSION;
		this.displayTime = DEFAULT_DISPLAYTIME;
		this.imageName = null;
		this.columns = DEFAULT_ARRANGEMENT;
		this.rows = DEFAULT_ARRANGEMENT;
		this.firstFrame = 0;
		this.nFrames = 0;
	}
	
	public void parse(XMLStreamReader reader) throws XMLStreamException, ParseException {
		reset();
		
		if (isStart(reader)) {
			this.width = parseDimension(reader, WIDTH_ATTRIB);
			this.height = parseDimension(reader, HEIGHT_ATTRIB);
			parseFramerate(reader);
			parseImageName(reader);
			this.columns = parseArrangement(reader, COLUMNS_ATTRIB);
			this.rows = parseArrangement(reader, ROWS_ATTRIB);
			parseSequence(reader);
		} else {
			throw new ParseException("xml element not found");
		}
	}
	
	private void parseSequence(XMLStreamReader reader) throws ParseException {
		String str = reader.getAttributeValue(NAME_SPACE, FIRST_FRAME_ATTRIB);
		
		if (str == null) {
			this.firstFrame = 0;
		} else {			
			try {
				int value = Integer.parseInt(str);
				if (value < 0 || value > this.columns * this.rows) {
					throw new ParseException("invalid value for first frame '" + str + "'");				
				}
				this.firstFrame = value;
			} catch (NumberFormatException e) {
				throw new ParseException("invalid value for first frame '" + str + "'");				
			}		
		}
		
		str = reader.getAttributeValue(NAME_SPACE, NUM_FRAMES_ATTRIB);
		
		if (str == null) {
			this.nFrames = this.rows * this.columns - this.firstFrame;
			return;
		}
		
		try {
			int value = Integer.parseInt(str);
			if (value <= 0 || this.columns * this.rows - this.firstFrame < value) {
				throw new ParseException("invalid value for number of frames '" + str + "'");				
			}
			this.nFrames = value;
		} catch (NumberFormatException e) {
			throw new ParseException("invalid value for number of frames '" + str + "'");				
		}		
	}

	private void parseImageName(XMLStreamReader reader) {
		this.imageName = reader.getAttributeValue(NAME_SPACE, IMAGE_ATTRIB);
	}
	
	private int parseArrangement(XMLStreamReader reader, String attrib) throws ParseException {
		String str = reader.getAttributeValue(NAME_SPACE, attrib);
		
		if (str == null) {
			return DEFAULT_ARRANGEMENT;
		}
		
		try {
			int value = Integer.parseInt(str);
			if (value <= 0) {
				throw new ParseException("invalid value for animation arrangment '" + str + "'");				
			}
			return value;
		} catch (NumberFormatException e) {
			throw new ParseException("invalid value for animation arrangment '" + str + "'");				
		}		
	}
	

	private double parseDimension(XMLStreamReader reader, String attrib) throws ParseException {
		String str = reader.getAttributeValue(NAME_SPACE, attrib);
		
		if (str == null) {
			return DEFAULT_DIMENSION;
		}
		
		try {
			double value = Double.parseDouble(str);
			if (value < 0) {
				throw new ParseException("invalid value for animation dimension '" + str + "'");				
			}
			return value;
		} catch (NumberFormatException e) {
			throw new ParseException("invalid value for sprite animation '" + str + "'");
		}		
	}

	private void parseFramerate(XMLStreamReader reader) throws ParseException {
		String str = reader.getAttributeValue(NAME_SPACE, FRAMERATE_ATTRIB);
		
		if (str == null) {
			this.displayTime = DEFAULT_DISPLAYTIME;
			return;
		}
		
		try {
			double value = Double.parseDouble(str);
			if (value <= 0) {
				throw new ParseException("invalid value for animation frame rate '" + str + "'");				
			}
			this.displayTime = 1.0 / value;
		} catch (NumberFormatException e) {
			throw new ParseException("invalid value for animation frame rate '" + str + "'");				
		}		
	}

	public double getWidth() {
		return width;
	}

	public double getHeight() {
		return height;
	}

	public String getImageName() {
		return imageName;
	}

	public double getDisplayTime() {
		return this.displayTime;
	}

	public int getColumns() {
		return columns;
	}

	public int getRows() {
		return rows;
	}

	public int getFirstFrame() {
		return firstFrame;
	}

	public int getNumFrames() {
		return nFrames;
	}
	
}
